package com.wyw.function.learning.demo7;

import java.util.function.Function;
import java.util.stream.LongStream;
import java.util.stream.Stream;

/**
 * @Title   并行数据处理和性能
 * @Description
 * @Author Mr Wu yewen.wu.china@gmail.com
 * Update History:
 * Author        Time            Content
 */
public class DemoClient17 {

    /**
     * 并行流内部默认使用ForkJoinPool，默认的线程数量就是处理器数量
     * 测试性能：测量对前n个自然数求和的函数的性能
     * 拆箱装箱会浪费性能，java8 使用LongStream，IntStream，DoubleStream 来避免
     * 某些操作在并行流上的性能比顺序流差，特别是limit和findFirst等依赖于元素顺序的操作，他们在并行流上的执行代价非常大，例如findAny比findFirst性能好，可以用unordered方法吧有序流改变成无序流
     * 要考虑流的操作流水线的总计算成本，设N是要处理的元素总数Q是一个元素通过流水线的大致处理成本，则N*Q就是对成本的粗略估计。Q值较高意味着使用并行流时性能好的可能性较大
     * 数据量较小的，使用并行不是一个好的决定
     * 使用并行要考虑流背后是否易于分解。例如ArrayList的拆分效率比LinkedList高得多，因为前者不需要遍历就可以分解。用range工厂创建的原始类型流也可以快速分解。
     * 流自身的特点，以及流水线中的中间操作修改流的方式，都可能会改变分解过程的性能。例如：一个SIZED流可以分成大小相等的量部分，这样每部分都可以比较高效地并行处理，但筛选操作可能丢弃地元素个数却无法预测，导致流本身地大小未知
     * 还要考虑终端操作中合并步骤地代价是大是小（例如Collector中combiner方法）。如果此步代价很大，那么组合每个子流产生地部分结果所敷出地代价就可能会超出通过并行流得到的性能提升    ArrayList-极佳    LinkedList-差    IntStream.range-极佳    Stream.iterate-差    HashSet\TreeSet-好
     */
    public static void main(String[] args) {
        // 无包装      4:ms
        System.out.println(measureSumPerf(o -> getForSum(o), 10000000l) + ":ms");
        // 顺序流      107:ms
        System.out.println(measureSumPerf(o -> getSum(o), 10000000l) + ":ms");
        // 并行流      89:ms
        System.out.println(measureSumPerf(o -> getParallelSum(o), 10000000l) + ":ms");
        // 更快的方案    1:ms
        System.out.println(measureSumPerf(o -> getFasterForSum(o), 10000000l) + ":ms");
    }

    private static Long measureSumPerf(Function<Long, Long> adder, Long n) {
        long fastest = Long.MAX_VALUE;
        for (int i = 0; i < 10; i++) {
            long start = System.currentTimeMillis();
            adder.apply(n);
            long end = System.currentTimeMillis();
            if((end - start) < fastest) {
                fastest = end - start;
            }
        }
        return fastest;
    }

    private static Long getForSum(Long n) {
        return LongStream.rangeClosed(0, n).sum();
    }

    private static Long getSum(Long n) {
        return Stream.iterate(1L, i -> i + 1).limit(n).reduce(0L, Long::sum);// iterate生成的是装箱对象，必须拆箱成数字才能求和
    }

    private static Long getParallelSum(Long n) {
        return Stream.iterate(1L, i -> i + 1).limit(n).parallel().reduce(0L, Long::sum);
    }

    private static Long getFasterForSum(Long n) {
        return LongStream.rangeClosed(0, n).parallel().sum();
    }


}
